﻿using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;

namespace Expect.Report.Word
{
    static class OpenXMLHelpers
    {
        public static void ValidateWordDocument(string filepath)
        {
            try
            {
                OpenXmlValidator validator = new OpenXmlValidator();
                int count = 0;
                foreach (ValidationErrorInfo error in
                    validator.Validate(
                        WordprocessingDocument.Open(filepath, true)))
                {
                    count++;
                    Console.WriteLine("Error " + count);
                    Console.WriteLine("Description: " + error.Description);
                    Console.WriteLine("ErrorType: " + error.ErrorType);
                    Console.WriteLine("Node: " + error.Node);
                    Console.WriteLine("Path: " + error.Path.XPath);
                    Console.WriteLine("Part: " + error.Part.Uri);
                    Console.WriteLine("-------------------------------------------");
                }
                //Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public static Paragraph NewParagraph(this WordprocessingDocument doc, string stylename)
        {
            Paragraph current = new Paragraph();

            if (!string.IsNullOrEmpty(stylename))
            {
                ParagraphProperties paragraphProperties1 = new ParagraphProperties();
                ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = stylename };
                paragraphProperties1.Append(paragraphStyleId1);
                current.Append(paragraphProperties1);
            }

            doc.MainDocumentPart.Document.Body.Append(current);

            return current;
        }

        public static void AddParagraph(this WordprocessingDocument doc, string text, string stylename)
        {
            Paragraph current = NewParagraph(doc, stylename);
            Run run1 = new Run();
            run1.Append(new Text { Text = text });
            current.Append(run1);
        }

        public static void AddParagraph(this WordprocessingDocument doc, string text)
        {
            AddParagraph(doc, text, string.Empty);
        }

        /// <summary>
        /// Sample to replace a header in a file from another file
        /// </summary>
        /// <param name="filepathFrom"></param>
        /// <param name="filepathTo"></param>
        public static void AddHeaderFromTo(string filepathFrom, string filepathTo)
        {
            // Replace header in target document with header of source document.
            using (WordprocessingDocument
                wdDoc = WordprocessingDocument.Open(filepathTo, true))
            {
                MainDocumentPart mainPart = wdDoc.MainDocumentPart;

                // Delete the existing header part.
                mainPart.DeleteParts(mainPart.HeaderParts);

                // Create a new header part.
                var headerPart = mainPart.AddNewPart<HeaderPart>();

                // Get Id of the headerPart.
                string rId = mainPart.GetIdOfPart(headerPart);

                // Feed target headerPart with source headerPart.
                using (WordprocessingDocument wdDocSource =
                    WordprocessingDocument.Open(filepathFrom, true))
                {
                    var firstHeader =
                        wdDocSource.MainDocumentPart.HeaderParts.FirstOrDefault();

                    if (firstHeader != null)
                    {
                        headerPart.FeedData(firstHeader.GetStream());
                    }
                }

                // Get SectionProperties and Replace HeaderReference with new Id.
                var sectPrs = mainPart.Document.Body.Elements<SectionProperties>();
                foreach (var sectPr in sectPrs)
                {
                    // Delete existing references to headers.
                    sectPr.RemoveAllChildren<HeaderReference>();

                    // Create the new header reference node.
                    sectPr.PrependChild<HeaderReference>(new HeaderReference() { Id = rId });
                }
            }
        }

        public static void UpdateProperty(this WordprocessingDocument document, string propname, string propval)
        {
            document.MainDocumentPart.RootElement.UpdateProperty(propname, propval);

            foreach (var part in document.MainDocumentPart.FooterParts)
            {
                part.RootElement.UpdateProperty(propname, propval);
            }
            foreach (var part in document.MainDocumentPart.HeaderParts)
            {
                part.RootElement.UpdateProperty(propname, propval);
            }
        }

        private static void UpdateProperty(this OpenXmlElement parentelement, string propname, string propval)
        {
            foreach (var element in parentelement.ChildElements)
            {
                if (element is FieldCode)
                {
                    FieldCode elt = element as FieldCode;
                    if (elt.Text.StartsWith(" DOCPROPERTY  " + propname + " "))
                    {
                        bool fieldFound = false;
                        bool startFound = false;
                        var root = parentelement.Parent;
                        for (int i = 0; i < root.ChildElements.Count; i++)
                        {
                            if (root.ChildElements[i] == parentelement)
                            {
                                fieldFound = true;
                                continue;
                            }
                            else if (fieldFound && !startFound && root.ChildElements[i] is Run)
                            {
                                Run r = (Run)root.ChildElements[i];
                                startFound = r.ChildElements.Any(e => e is FieldChar && ((FieldChar)e).FieldCharType == FieldCharValues.Separate);
                            }
                            else if (startFound && root.ChildElements[i] is Run)
                            {
                                Run r = (Run)root.ChildElements[i];
                                var txt = (Text)r.ChildElements.FirstOrDefault(e => e is Text);
                                if (txt != null)
                                {
                                    txt.Text = propval;
                                    break;
                                }

                            }
                        }
                    }
                }
                else if (element is SimpleField)
                {
                    SimpleField elt = element as SimpleField;
                    if (elt.Instruction.Value.StartsWith(" DOCPROPERTY  " + propname + " "))
                    {
                        SetElementValue(elt, propval);
                    }
                }
                else
                {
                    UpdateProperty(element, propname, propval);
                }
            }
        }

        private static void SetElementValue(OpenXmlElement elt, string propval)
        {
            var run = elt.ChildElements.FirstOrDefault(e => e is Run);
            if (run != null)
            {
                Run r = new Run();
                Text t = new Text(propval);
                r.Append(t);
                elt.ReplaceChild(r, run);
            }
        }
    }
}
